/*
 * @Author: licon licon.ye@gmail.com
 * @Description: 企业微信消息通知模块 ，日记记录发送结果
 * @LastEditors: licon
 */
package wecom

import (
	"bytes"
	"encoding/json"
	"ferry/pkg/logger"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/go-redis/redis"
	"github.com/spf13/viper"
)

const (
	WecomTokenUrl   = "https://qyapi.weixin.qq.com/cgi-bin/gettoken"       // 获取企业微信 access_token 的接口
	WecomUserIdUrl  = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserid" //通过手机号获取企业微信用户id
	WecomSendMsgUrl = "https://qyapi.weixin.qq.com/cgi-bin/message/send"   //信息发送接口
)

// 数据接口定义
type WecomTokenResp struct {
	Errcode     int    `json:"errcode"`      //错误码，0表示成功，非0表示失败
	AccessToken string `json:"access_token"` //token内容
	ExpiresIn   int    `json:"expires_in"`   //有效时间，单位为秒
	Errmsg      string `json:"errmsg"`       //错误信息
}

type WecomUserIdReq struct {
	Mobile string `json:"mobile"` // 手机号
}

type WecomUserIdResp struct {
	Errcode int    `json:"errcode"`
	Userid  string `json:"userid"` // 企业微信用户id
	Errmsg  string `json:"errmsg"`
}

type WecomMsgBody struct {
	Touser   string `json:"touser"`  //接受人，多个用"|"分隔
	Msgtype  string `json:"msgtype"` //消息类型（如markdown）
	Agentid  int64  `json:"agentid"` //企业微信应用id
	Markdown *struct {
		Content string `json:"content"` //markdown内容
	} `json:"markdown,omitempty"`
}

type WecomSendMsgResp struct {
	Errcode int    `json:"errcode"`
	Errmsg  string `json:"errmsg"`
}

// 根据企业微信corpid和corpsecret获取access_token
// 成功返回token，否则返回空字符串
func GetWecomAccessToken(corpid, corpsecret string) string {
	res, err := http.Get(fmt.Sprintf("%s?corpid=%s&corpsecret=%s", WecomTokenUrl, corpid, corpsecret))
	if err != nil {
		logger.Info(err)
		return ""
	}
	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		logger.Info(err)
		return ""
	}
	var token WecomTokenResp
	json.Unmarshal(body, &token)
	if token.Errcode == 0 {
		return token.AccessToken
	} else {
		logger.Info(token.Errmsg)
		return ""
	}
}

// 用access_token和手机号获取企业微信用户id
// 通过手机号查找企业微信用户id
func GetWecomUserId(token string, mobile string) string {
	b, _ := json.Marshal(WecomUserIdReq{Mobile: mobile})
	resp, err := http.Post(fmt.Sprintf("%s?access_token=%s", WecomUserIdUrl, token), "application/json", bytes.NewBuffer(b))
	if err != nil {
		logger.Info(err)
		return ""
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	var jsonBody WecomUserIdResp
	json.Unmarshal(body, &jsonBody)
	if jsonBody.Errcode == 0 {
		return jsonBody.Userid
	} else {
		logger.Info(jsonBody.Errmsg)
		return ""
	}
}

// 发送企业微信消息
// 用access_token发送Markdown 格式消息到指定用户（touser），支持多用户
func SendWecomMsg(token string, content WecomMsgBody) error {
	b, _ := json.Marshal(content)
	resp, err := http.Post(fmt.Sprintf("%s?access_token=%s", WecomSendMsgUrl, token), "application/json", bytes.NewBuffer(b))
	if err != nil {
		logger.Info(err)
		return err
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	var jsonBody WecomSendMsgResp
	json.Unmarshal(body, &jsonBody)
	if jsonBody.Errcode != 0 {
		logger.Info(jsonBody.Errmsg)
	}
	return nil
}

// 发送到企业微信通知（主入口）
// 发送企业微信通知
func SendWecomNotify(phoneList []string, url, msgTitle, msgCreator, priority, createdAt string) {
	corpid := viper.GetString("settings.wecom.corpid")
	corpsecret := viper.GetString("settings.wecom.corpsecret")
	agentid := viper.GetInt64("settings.wecom.agentid")

	//连接redis，用于缓存access_token
	client := redis.NewClient(&redis.Options{
		Addr:     viper.GetString("settings.redis.host"),
		Password: viper.GetString("settings.redis.pwd"),
		DB:       1,
	})

	var token string
	tokenByte, err := client.Get("accessTokenWecom").Result()
	if err != nil {
		// redis 没有缓存，最新获取 token 并缓存
		logger.Info(err)
		token = GetWecomAccessToken(corpid, corpsecret)
		err = client.Set("accessTokenWecom", token, 110*time.Minute).Err()
		if err != nil {
			logger.Info(err)
		}
	} else {
		token = string(tokenByte)
	}

	var uids []string
	for _, phone := range phoneList {
		uid := GetWecomUserId(token, phone)
		if uid != "" {
			uids = append(uids, uid)
		}
	}
	if len(uids) == 0 {
		return //没有有效用户id，直接返回
	}
	uidsString := strings.Join(uids, "|")
	nowStr := time.Now().Format("2006-01-02 15:04:05")
	content := WecomMsgBody{
		Touser:  uidsString,
		Msgtype: "markdown",
		Agentid: agentid,
		Markdown: &struct {
			Content string `json:"content"`
		}{
			Content: fmt.Sprintf("# 你有一个待办项目需要处理：\n**标题：** %s\n**创建人：** %s\n**优先级：** %s\n**创建时间：** %s\n**当前时间：** %s\n[去处理](%s)", msgTitle, msgCreator, priority, createdAt, nowStr, url),
		},
	}
	err = SendWecomMsg(token, content)
	if err != nil {
		logger.Info(err)
	}
	logger.Info("send wecom notify successfully")
}
